#include <rtthread.h>
#include <rtdevice.h>
#include "drv_common.h"
#include "app_main.h"
#include <at.h>

#ifdef APP_USING_L610

#define DBG_TAG "l610        "
#define DBG_LVL DBG_INFO
#include <rtdbg.h>

static rt_device_t device;
static at_response_t resp;

/**
 * @brief 网络连接成功回调
 */
static void net_connected_cb(at_client_t at_client, const char *data, rt_size_t size)
{
    LOG_I("net ready.");
    rt_event_send(s_event, EVENT_L610_NET_READY);
}

/**
 * @brief MQTT连接成功回调
 */
static void mqtt_connected_cb(at_client_t at_client, const char *data, rt_size_t size)
{
    LOG_I("mqtt ready.");
rt_event_send(s_event, EVENT_L610_MQTT_READY);
}

/**
 * @brief 发布消息回调
 */
static void mqtt_puback_db(at_client_t at_client, const char *data, rt_size_t size)
{
    LOG_D("MQTT PUBACK: %s", data);
    if (strcmp(data, "+MQTTPUB: 1,1\r\n") == 0)
    {
        LOG_I("mqtt publish success.");
        rt_event_send(s_event, EVENT_L610_PUBLISHED);
    }
}

/**
 * @brief 联网与连接MQTT服务器线程
 *
 * @note 内部流程：
 *         1. 发送`AT+MIPCALL?`，回复`+MIPCALL: 1,ip地址`表示已联网
 *         2. 发送`AT+MQTTUSER=1,"用户名","密码"`，回复`OK`
 *         3. 发送`AT+MQTTOPEN=1,"域名或地址",端口号,0,60`，回复`ERROR`表示已经连接MQTT服务器；
 *                         回复`OK`、上报`+MQTTPUB: 1,1`表示成功连接MQTT服务器
 */
static void connect_thread(void *params)
{
    /* 判断是否已联网 */
    while (1)
    {
        /* URC表中读出了一条，故读取行数为3 */
        resp = at_resp_set_info(resp, 512, 3, rt_tick_from_millisecond(1000));
        at_exec_cmd(resp, "AT+MIPCALL?");

        if (rt_event_recv(s_event, EVENT_L610_NET_READY, RT_EVENT_FLAG_OR, 0, NULL) == RT_EOK)
        {

            break;
        }
        rt_thread_mdelay(2000);
    };

    while (1)
    {
        resp = at_resp_set_info(resp, 512, 2, rt_tick_from_millisecond(1000));
        at_exec_cmd(resp, "AT+MQTTUSER=1,\""APP_MQTT_USERNAME"\",\""APP_MQTT_PASSWROD"\"");
        at_exec_cmd(resp, "AT+MQTTOPEN=1,\""APP_MQTT_HOSTNAME"\","APP_MQTT_PORT",0,60");

        if (rt_event_recv(s_event, EVENT_L610_MQTT_READY, RT_EVENT_FLAG_OR, 3000, NULL) == RT_EOK)
        {
            break;
        }

        /* fix bug: not `\r\n` */
        if(strcmp(at_resp_get_line(resp, 2), "ERROR\r") == 0)
        {
            mqtt_connected_cb(NULL, NULL, 0);
            break;
        }
    }

    /* TODO: 检测网络断开，重连 */
}

/**
 * @brief 发布mqtt消息，如果MQTT未连接将阻塞等到MQTT连接成功
 *
 * @param msg 消息指针
 *
 * @return 0代表成功否则失败
 */
int l610_mqtt_publish(uint8_t *msg)
{
    static uint8_t buff[128];

    /* 等待MQTT连接成功 */
    rt_event_recv(s_event, EVENT_L610_MQTT_READY, RT_EVENT_FLAG_OR, RT_WAITING_FOREVER, NULL);

    sprintf(buff, "AT+MQTTPUB=1,\"%s\",0,0,%d", APP_MQTT_PUBLISH_TOPIC, strlen(msg));

    resp = at_resp_set_info(resp, 512, 2, rt_tick_from_millisecond(1000));
    at_exec_cmd(resp, buff);
    rt_thread_mdelay(300);
    at_exec_cmd(resp, msg);
    rt_event_recv(s_event, EVENT_L610_PUBLISHED, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 0, NULL);
    resp = at_resp_set_info(resp, 512, 2, rt_tick_from_millisecond(30000));

    if (rt_event_recv(s_event, EVENT_L610_PUBLISHED, RT_EVENT_FLAG_OR, 1000, NULL) == 0)
        return 0;

    return -1;
}

/**
 * @brief 通过命令行调试模块
 *
 * @param argc 参数个数
 * @param argv 参数值数组
 *
 * @code
 * test_l610_cmd AT\n
 * @endcode
 */
static void test_l610_cmd(int argc, char**argv)
{
    if (argc == 2)
    {
        resp = at_resp_set_info(resp, 512, 3, rt_tick_from_millisecond(3000));
        at_exec_cmd(resp, argv[1]);
    }

}
MSH_CMD_EXPORT(test_l610_cmd, send cmd to l610);

/**
 * @brief URC上报处理表
 */
struct at_urc urc_table[] = { { "+MIPCALL: 1,", "\r\n", net_connected_cb }, // 网络已连接
        { "+MQTTOPEN: 1,1", "\r\n", mqtt_connected_cb }, // MQTT已连接
        { "+MQTTPUB: 1,", "\r\n", mqtt_puback_db }, // MQTT发布消息回应
        };

/**
 * @brief 4G模块初始化
 *
 * @return 0代表成功否则失败
 */
int l610_init()
{
    device = rt_device_find(L610_DEV_NAME);
    rt_device_open(device, RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX);

    at_client_init(L610_DEV_NAME, 512);
    at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
    resp = at_create_resp(512, 0, rt_tick_from_millisecond(300));

    /* 运行OS调度命令行线程，避免打印`msh>`干扰调试 */
    rt_thread_mdelay(10);

    rt_thread_t thread = rt_thread_create("4G", connect_thread, NULL, 2048, PRIO_NBIOT, 10);
    rt_thread_startup(thread);

    return 0;
}
INIT_APP_EXPORT(l610_init);
#endif /* APP_USING_L610 */
